//
// PageFrameAllocator.cpp
// Usage: Implement of pageframe allocator
// Create Date: 2009-03-03
// Author: MaJiuyue
//
#include "../core/ResourceManager.h"
#include "SCI.h"
#include "PageFrameAllocator.h"
#include "MemoryZone.h"
#include "bootutil.h"

#define page_address(page)	((page-mem_map)<<12)

Page * CPageFrameAllocator::mem_map = NULL;
MemoryZone CPageFrameAllocator::mem_zone[2] = {
	MemoryZone("DMA", 0, 0x1000, 8),
	MemoryZone("Normal", 0x1000, PHYMAP_END/PAGE_SIZE-0x1000, 32)
};

CPageFrameAllocator::CPageFrameAllocator(int pg_count)
{
	// Allocate memory for page descriptor
	mem_map = new Page[pg_count];

	// Set reserved page
	mem_map[0].SetReserved();
	for(int i=160; i<256; i++)
		mem_map[i].SetReserved();
	for(int i=KERNEL_RESERVED_START/PAGE_SIZE; i<KERNEL_RESERVED_END/PAGE_SIZE; i++)
		mem_map[i].SetReserved();

	// Init each zone
	for(int i=0; i<ZONE_NR; i++)
		mem_zone[i].Initialize(mem_map);

	// Create `PageFrameAllocator' resource
	Resource * res = new Resource(this);
	ResourceManager.CreateResource(res, ServiceCallInterface::PageFrameAllocator::ResourceName); 
	res->RegisterInterface(
		(Resource::CallInterface1)&CPageFrameAllocator::GetPagesDesc,
		ServiceCallInterface::PageFrameAllocator::SCIID_GetPagesDesc
	);
	res->RegisterInterface(
		(Resource::CallInterface1)&CPageFrameAllocator::GetPagesAddr,
		ServiceCallInterface::PageFrameAllocator::SCIID_GetPagesAddr
	);
	res->RegisterInterface(
		(Resource::CallInterface0)&CPageFrameAllocator::GetPageDesc,
		ServiceCallInterface::PageFrameAllocator::SCIID_GetPageDesc
	);
	res->RegisterInterface(
		(Resource::CallInterface0)&CPageFrameAllocator::GetPageAddr,
		ServiceCallInterface::PageFrameAllocator::SCIID_GetPageAddr
	);
	res->RegisterInterface(
		(Resource::CallInterface0)&CPageFrameAllocator::GetZeroedPageDesc,
		ServiceCallInterface::PageFrameAllocator::SCIID_GetZeroedPageDesc
	);
	res->RegisterInterface(
		(Resource::CallInterface0)&CPageFrameAllocator::GetZeroedPageAddr,
		ServiceCallInterface::PageFrameAllocator::SCIID_GetZeroedPageAddr
	);
	res->RegisterInterface(
		(Resource::CallInterface1)&CPageFrameAllocator::GetDMAPagesDesc,
		ServiceCallInterface::PageFrameAllocator::SCIID_GetDMAPagesDesc
	);
	res->RegisterInterface(
		(Resource::CallInterface1)&CPageFrameAllocator::GetDMAPagesAddr,
		ServiceCallInterface::PageFrameAllocator::SCIID_GetDMAPagesAddr
	);
	res->RegisterInterface(
		(Resource::CallInterface1)&CPageFrameAllocator::FreePagesDesc,
		ServiceCallInterface::PageFrameAllocator::SCIID_FreePagesDesc
	);
	res->RegisterInterface(
		(Resource::CallInterface2)&CPageFrameAllocator::FreePagesAddr,
		ServiceCallInterface::PageFrameAllocator::SCIID_FreePagesAddr
	);
	res->RegisterInterface(
		(Resource::CallInterface1)&CPageFrameAllocator::FreePageDesc,
		ServiceCallInterface::PageFrameAllocator::SCIID_FreePageDesc
	);
	res->RegisterInterface(
		(Resource::CallInterface1)&CPageFrameAllocator::FreePageAddr,
		ServiceCallInterface::PageFrameAllocator::SCIID_FreePageAddr
	);
}

Page * CPageFrameAllocator::GetPagesDesc(int order)
{
	// Allocate page from matched zone
	return mem_zone[1].RemoveQueue(order);
}

void CPageFrameAllocator::FreePagesDesc(Page *page, int order)
{
	for(int i=0; i<ZONE_NR; i++)
	{
		if(mem_zone[i].IsContain(page))
		{
			mem_zone[i].FreePagesBulk(page, order);
			return;
		}
	}
}

void CPageFrameAllocator::FreePagesAddr(addr_t addr, int order)
{
	u32 page_idx = addr>>12;
	Page * page = &mem_map[page_idx];
	FreePagesDesc(page, order);
}

Page * CPageFrameAllocator::GetZeroedPageDesc()
{
	Page * page;
	page = GetPagesDesc(0);

	// Do clear operation
	if(page!=NULL)
	{
		u32 * p = (u32 *)page_address(page);	
		for(int i=0; i<PAGE_SIZE/sizeof(u32); i++)
			*p++ = 0;	
	}

	return page;
}

addr_t CPageFrameAllocator::GetZeroedPageAddr()
{
	Page * page;

	page = GetZeroedPageDesc(); 
	if (page)
		return (addr_t)page_address(page);
	return 0;
}

Page * CPageFrameAllocator::GetDMAPagesDesc(int order)
{
	return mem_zone[0].RemoveQueue(order);
}

addr_t CPageFrameAllocator::GetDMAPagesAddr(int order)
{	
	Page * page;
	page = GetDMAPagesDesc(order);
	if (page)
		return (addr_t) page_address(page);
	return 0;
}

